package co.yixiang.yshop.server.chaos;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 混沌工程过滤器：注入延迟、CPU 重度计算、大内存分配
 * 注意：内存分配不设 OOM 防护，用于真实模拟资源耗尽场景。
 */
public class ChaosFilter extends OncePerRequestFilter {

    private static final Logger log = LoggerFactory.getLogger(ChaosFilter.class);

    /**
     * 防止 JIT 优化掉无副作用的计算循环
     */
    private static volatile double sink;

    private final ChaosProperties properties;
    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    public ChaosFilter(ChaosProperties properties) {
        this.properties = properties;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        boolean applied = applyIfNeeded(request);
        try {
            filterChain.doFilter(request, response);
        } finally {
            if (applied) {
                log.debug("ChaosFilter applied to path: {}", request.getRequestURI());
            }
        }
    }

    private boolean applyIfNeeded(HttpServletRequest request) {
        if (!properties.isEnable()) {
            return false;
        }

        String uri = request.getRequestURI();
        if (!matchesInclude(uri) || matchesExclude(uri)) {
            return false;
        }

        // 概率触发
        double p = Math.max(0.0, Math.min(1.0, properties.getProbability()));
        if (p > 0.0 && ThreadLocalRandom.current().nextDouble() > p) {
            return false;
        }

        // 注入延迟
        if (properties.getDelayMs() > 0) {
            try {
                Thread.sleep(properties.getDelayMs());
            } catch (InterruptedException ignored) {
                Thread.currentThread().interrupt();
            }
        }

        // 重度 CPU 计算
        injectCpuLoad();

        // 大内存分配（无 OOM 防护）
        injectMemoryAlloc();

        return true;
    }

    /**
     * 执行重度 CPU 计算，持续占用一个 CPU 核心
     */
    private void injectCpuLoad() {
        long cpuSpinMs = properties.getCpuSpinMs();
        if (cpuSpinMs <= 0) return;

        long endNanos = System.nanoTime() + cpuSpinMs * 1_000_000L;
        double[] buffer = new double[64];
        for (int i = 0; i < buffer.length; i++) {
            buffer[i] = i + 1.0;
        }

        double sum = 0.0;
        long iterations = 0;

        while (System.nanoTime() < endNanos) {
            for (int i = 0; i < buffer.length; i++) {
                double val = buffer[i];
                val = Math.sqrt(Math.pow(val, 3) + 2.71828);
                val = Math.sin(val * 0.1) + Math.cos(val * 0.05);
                val = Math.log(val + 1e-8) * 1.4142 + Math.exp(-val * 0.01);
                buffer[i] = val;
                sum += val;
            }

            iterations++;
            if ((iterations & 0xFF) == 0) {
                double[] m = {sum, 1.0, 2.0, 3.0, sum, 4.0, 5.0, 6.0, sum};
                double[] res = new double[9];
                for (int i = 0; i < 3; i++) {
                    for (int j = 0; j < 3; j++) {
                        for (int k = 0; k < 3; k++) {
                            res[i * 3 + j] += m[i * 3 + k] * m[k * 3 + j];
                        }
                    }
                }
                sum += res[0];
            }
        }

        sink = sum + buffer[32]; // 强引用防止优化
    }

    /**
     * 分配指定大小的内存（上限 256MB），不进行异常防护
     * 若内存不足，直接抛出 OutOfMemoryError
     */
    private void injectMemoryAlloc() {
        int kb = properties.getAllocateKb();
        if (kb <= 0) return;

        int maxSizeKb = 256 * 1024; // 256 MB
        int sizeKb = Math.min(kb, maxSizeKb);
        int size = sizeKb * 1024; // 转为字节

        byte[] tmp = new byte[size];

        // 触发实际物理内存使用（按页访问）
        for (int i = 0; i < size; i += 4096) {
            tmp[i] = (byte) (i & 0xFF);
        }
    }

    private boolean matchesInclude(String uri) {
        List<String> include = properties.getIncludePaths();
        if (include == null || include.isEmpty()) {
            return true;
        }
        return include.stream().anyMatch(pattern -> pathMatcher.match(pattern, uri));
    }

    private boolean matchesExclude(String uri) {
        List<String> exclude = properties.getExcludePaths();
        if (exclude == null || exclude.isEmpty()) {
            return false;
        }
        return exclude.stream().anyMatch(pattern -> pathMatcher.match(pattern, uri));
    }
}